package net.qiqbframework.config;

import lombok.extern.slf4j.Slf4j;
import net.qiqbframework.commandhandling.*;
import net.qiqbframework.commandhandling.gateway.CommandGateway;
import net.qiqbframework.commandhandling.gateway.DefaultCommandGateway;
import net.qiqbframework.common.Registration;
import net.qiqbframework.common.security.AuthUserProvider;
import net.qiqbframework.common.transaction.NoTransactionManager;
import net.qiqbframework.common.transaction.TransactionManager;
import net.qiqbframework.eventhandling.DefaultEventProcessorSpanFactory;
import net.qiqbframework.eventhandling.EventBus;
import net.qiqbframework.eventhandling.EventProcessorSpanFactory;
import net.qiqbframework.eventhandling.SimpleEventBus;
import net.qiqbframework.eventhandling.distributing.DefaultEventDistributor;
import net.qiqbframework.eventhandling.distributing.EventChannel;
import net.qiqbframework.eventhandling.distributing.EventDistributor;
import net.qiqbframework.eventhandling.distributing.LocalEventChannel;
import net.qiqbframework.eventhandling.gateway.DefaultEventGateway;
import net.qiqbframework.eventhandling.gateway.EventGateway;
import net.qiqbframework.lifecycle.LifecycleHandler;
import net.qiqbframework.lifecycle.LifecycleHandlerInvocationException;
import net.qiqbframework.loadhanding.AggregateDataProvider;
import net.qiqbframework.loadhanding.AggregateLoadBus;
import net.qiqbframework.loadhanding.AggregateLoadBusImplBuilder;
import net.qiqbframework.loadhanding.AnnotationLoadHandlerAdapter;
import net.qiqbframework.messaging.Message;
import net.qiqbframework.messaging.annotation.*;
import net.qiqbframework.messaging.correlation.CorrelationDataProvider;
import net.qiqbframework.messaging.correlation.MessageOriginProvider;
import net.qiqbframework.messaging.intercepting.CorrelationDataInterceptor;
import net.qiqbframework.messaging.monitoring.MessageMonitor;
import net.qiqbframework.modelling.domain.AggregateModelFactory;
import net.qiqbframework.modelling.domain.AnnotatedAggregateModelFactory;
import net.qiqbframework.persisthanding.AggregatePersistBus;
import net.qiqbframework.persisthanding.AggregatePersistBusBuilder;
import net.qiqbframework.persisthanding.AnnotationPersistHandlerAdapter;
import net.qiqbframework.persisthanding.PersistedFlagDataProvider;
import net.qiqbframework.serialization.Serializer;
import net.qiqbframework.tracing.NoOpSpanFactory;
import net.qiqbframework.tracing.SpanFactory;
import org.javers.core.JaversBuilder;

import javax.annotation.Nonnull;
import java.util.*;
import java.util.concurrent.*;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;

import static java.util.stream.Collectors.toList;
import static net.qiqbframework.common.BuilderUtils.assertNonNull;
import static net.qiqbframework.common.BuilderUtils.assertStrictPositive;
import static net.qiqbframework.common.utils.HandlerTypeResolver.isCommandHandler;
import static net.qiqbframework.common.utils.HandlerTypeResolver.isEventHandler;

@Slf4j
public class DefaultConfigurer implements Configurer {

    private static final Runnable NOTHING = () -> {
    };

    private final Configuration config = new DefaultConfigurationImpl(this);

    private final MessageMonitorFactoryBuilder messageMonitorFactoryBuilder = new MessageMonitorFactoryBuilder();
    protected final ComponentHolder<BiFunction<Class<?>, String, MessageMonitor<Message<?>>>> messageMonitorFactoryComponent = new ComponentHolder<>(config, "monitorFactory", messageMonitorFactoryBuilder::build);
    protected final ComponentHolder<List<CorrelationDataProvider>> correlationProviders = new ComponentHolder<>(config, "correlationProviders", c -> List.of(new MessageOriginProvider(), new AggregateDataProvider(), new PersistedFlagDataProvider()));
    protected final Map<Class<?>, ComponentHolder<?>> components = new ConcurrentHashMap<>();
    private final List<ComponentHolder<MessageHandlerRegistrar>> messageHandlerRegistrars = new ArrayList<>();
    // 事件序列化
    protected final ComponentHolder<Serializer> eventSerializer = new ComponentHolder<>(config, "eventSerializer", Configuration::eventSerializer);
    protected final ComponentHolder<Serializer> messageSerializer = new ComponentHolder<>(config, "messageSerializer", Configuration::serializer);
    protected final ComponentHolder<Serializer> aggregateSerializer = new ComponentHolder<>(config, "aggregateSerializer", Configuration::serializer);

    protected final ComponentHolder<Function<Class<?>, MessageHandlingMemberFactory>> handlerDefinition = new ComponentHolder<>(config, "handlerDefinition", c -> this::defaultHandlerDefinition);
    private final List<ComponentHolder<ExecutableHandlerWrapper>> handlerEnhancerDefinitions = new ArrayList<>();

    private final List<Consumer<Configuration>> initHandlers = new ArrayList<>();
    protected final TreeMap<Integer, List<LifecycleHandler>> startHandlers = new TreeMap<>();
    protected final TreeMap<Integer, List<LifecycleHandler>> shutdownHandlers = new TreeMap<>(Comparator.reverseOrder());
    protected final List<ModuleConfiguration> modules = new ArrayList<>();
    private long lifecyclePhaseTimeout = 5;
    private TimeUnit lifecyclePhaseTimeunit = TimeUnit.SECONDS;
    /**
     * 是否完成初始化
     */
    private boolean initialized = false;
    protected Integer currentLifecyclePhase = null;
    protected LifecycleState lifecycleState = LifecycleState.DOWN;

    /**
     * Initialize the Configurer.
     */
    protected DefaultConfigurer() {
        components.put(HandlerParameterResolverFactory.class, new ComponentHolder<>(config, "parameterResolverFactory", this::defaultParameterResolverFactory));
        components.put(HandlerAttributesPostProcessor.class, new ComponentHolder<>(config, "handlerAttributesPostProcessor", this::defaultHandlerAttributesPostProcessor));
        // components.put(Serializer.class, new Component<>(config, "serializer", this::defaultSerializer));
        components.put(CommandBus.class, new ComponentHolder<>(config, "commandBus", this::defaultCommandBus));
        components.put(EventDistributor.class, new ComponentHolder<>(config, "eventDistributor", this::defaultEventDistributor));

        components.put(EventBus.class, new ComponentHolder<>(config, "eventBus", this::defaultEventBus));
        components.put(AggregateLoadBus.class, new ComponentHolder<>(config, "aggregateLoadBus", this::defaultAggregateLoadBus));
        components.put(AggregatePersistBus.class, new ComponentHolder<>(config, "aggregatePersistBus", this::defaultAggregatePersistBus));

        components.put(CommandGateway.class, new ComponentHolder<>(config, "commandGateway", this::defaultCommandGateway));


        components.put(EventGateway.class, new ComponentHolder<>(config, "eventGateway", this::defaultEventGateway));
        components.put(TagsConfiguration.class, new ComponentHolder<>(config, "tags", c -> new TagsConfiguration()));


        components.put(SpanFactory.class, new ComponentHolder<>(config, "spanFactory", this::defaultSpanFactory));
        components.put(CommandBusSpanFactory.class, new ComponentHolder<>(config, "commandBusSpanFactory", this::defaultCommandBusSpanFactory));

        components.put(EventProcessorSpanFactory.class, new ComponentHolder<>(config, "eventProcessorSpanFactory", this::defaultEventProcessorSpanFactory));
        components.put(AuthUserProvider.class, new ComponentHolder<>(config, "authUserProvider", this::defaultAuthUserProvider));
        components.put(JaversBuilder.class, new ComponentHolder<>(config, "authUserProvider", this::defaultJaversBuilder));

    }


    public static Configurer defaultConfiguration() {
        return defaultConfiguration(true);
    }


    public static Configurer defaultConfiguration(boolean autoLocateConfigurerModules) {
        DefaultConfigurer configurer = new DefaultConfigurer();
        if (autoLocateConfigurerModules) {
            ServiceLoader<ConfigurerModule> configurerModuleLoader = ServiceLoader.load(ConfigurerModule.class, configurer.getClass().getClassLoader());
            List<ConfigurerModule> configurerModules = new ArrayList<>();
            configurerModuleLoader.forEach(configurerModules::add);
            configurerModules.sort(Comparator.comparingInt(ConfigurerModule::order));
            configurerModules.forEach(cm -> cm.configureModule(configurer));
        }
        return configurer;
    }


    /**
     * Method returning a default component to use for given {@code type} for given {@code configuration}, or an empty
     * Optional if no default can be provided.
     *
     * @param type          The type of component to find a default for.
     * @param configuration The configuration the component is configured in.
     * @param <T>           The type of component.
     * @return An Optional containing a default component, or empty if none can be provided.
     */
    protected <T> Optional<T> defaultComponent(Class<T> type, Configuration configuration) {
        return Optional.empty();
    }

    protected EventBus defaultEventBus(Configuration config) {

        return defaultComponent(EventBus.class, config).orElseGet(() -> new SimpleEventBus.Builder().build());
    }

    protected AggregateLoadBus defaultAggregateLoadBus(Configuration config) {
        final AggregateModelFactory aggregateModelFactory = config.getComponent(
                AggregateModelFactory.class,
                () -> new AnnotatedAggregateModelFactory(config.parameterResolverFactory(),
                        config.handlerDefinition(this.getClass()), config.javersBuilder())
        );
        return defaultComponent(AggregateLoadBus.class, config).orElseGet(() -> new AggregateLoadBusImplBuilder().aggregateModelFactory(aggregateModelFactory).build());
    }

    protected AggregatePersistBus defaultAggregatePersistBus(Configuration config) {

        return defaultComponent(AggregatePersistBus.class, config).orElseGet(() -> new AggregatePersistBusBuilder().build());
    }


    /**
     * Returns a {@link DefaultCommandGateway} that will use the configuration's {@link CommandBus} to dispatch
     * commands.
     *
     * @param config The configuration that supplies the command bus.
     * @return The default command gateway.
     */
    protected CommandGateway defaultCommandGateway(Configuration config) {
        return defaultComponent(CommandGateway.class, config).orElseGet(() -> DefaultCommandGateway.builder().commandBus(config.commandBus()).build());
    }

    /**
     * Provides the default ParameterResolverFactory. Subclasses may override this method to provide their own default.
     *
     * @param config The configuration based on which the component is initialized.
     * @return The default ParameterResolverFactory to use.
     */
    protected HandlerParameterResolverFactory defaultParameterResolverFactory(Configuration config) {
        return defaultComponent(HandlerParameterResolverFactory.class, config).orElseGet(() -> MultiParameterResolverFactory.ordered(ClasspathParameterResolverFactory.forClass(getClass()), new ConfigurationParameterResolverFactory(config)));
    }

    protected HandlerAttributesPostProcessor defaultHandlerAttributesPostProcessor(Configuration config) {
        return (e, h) -> h;
    }

    /**
     * Provides the default HandlerDefinition. Subclasses may override this method to provide their own default.
     *
     * @param inspectedClass The class being inspected for handlers
     * @return The default HandlerDefinition to use
     */
    protected MessageHandlingMemberFactory defaultHandlerDefinition(Class<?> inspectedClass) {
        MessageHandlingMemberFactory definition = defaultComponent(MessageHandlingMemberFactory.class, config).orElseGet(() -> ClasspathHandlerDefinition.forClass(inspectedClass));

        List<ExecutableHandlerWrapper> registeredEnhancerDefinitions = handlerEnhancerDefinitions.stream().map(ComponentHolder::get).collect(toList());
        if (definition instanceof MultiHandlerDefinition) {
            registeredEnhancerDefinitions.add(((MultiHandlerDefinition) definition).getHandlerEnhancerDefinition());
        }

        return MultiHandlerDefinition.ordered(Collections.singletonList(definition), MultiExecutableHandlerEnhancer.ordered(registeredEnhancerDefinitions));
    }

    /**
     * Provides the default CommandBus implementation. Subclasses may override this method to provide their own
     * default.
     *
     * @param config The configuration based on which the component is initialized.
     * @return The default CommandBus to use.
     */
    protected CommandBus defaultCommandBus(Configuration config) {
        return defaultComponent(CommandBus.class, config).orElseGet(() -> {
            CommandBus commandBus = new DefaultCommandBusBuilder(config)
                    // TransactionManager
                    .transactionManager(config.getComponent(TransactionManager.class, () -> NoTransactionManager.INSTANCE))
                    // duplicateCommandHandlerResolver
                    .duplicateCommandHandlerResolver(config.getComponent(DuplicateCommandHandlerResolver.class, DuplicateCommandHandlerResolution::rejectDuplicates))
                    // spanFactory
                    .spanFactory(config.getComponent(CommandBusSpanFactory.class))
                    // messageMonitor
                    .messageMonitor(config.messageMonitor(DefaultCommandBus.class, "commandBus"))
                    .build();
            commandBus.registerHandlerInterceptor(new CorrelationDataInterceptor<>(config.correlationDataProviders()));
            return commandBus;
        });
    }

    protected EventDistributor defaultEventDistributor(Configuration config) {
        return defaultComponent(EventDistributor.class, config).orElseGet(() -> {
            EventDistributor eventDistributor = new DefaultEventDistributor.Builder()
                    // eventChannel
                    .eventChannel(config.getComponent(EventChannel.class, () -> new LocalEventChannel(config.eventGateway())))
                    // build
                    .build();

            return eventDistributor;
        });
    }


    /**
     * Returns the default {@link SpanFactory}, or a {@link NoOpSpanFactory} if none it set.
     *
     * @param config The configuration that supplies the span factory.
     * @return The default {@link SpanFactory}.
     */
    protected SpanFactory defaultSpanFactory(Configuration config) {
        return defaultComponent(SpanFactory.class, config).orElseGet(NoOpSpanFactory::new);
    }

    protected EventGateway defaultEventGateway(Configuration config) {
        return defaultComponent(EventGateway.class, config).orElseGet(() -> DefaultEventGateway.builder().eventBus(config.eventBus()).build());
    }

    /**
     * Returns the default {@link CommandBusSpanFactory}, or a {@link DefaultCommandBusSpanFactory} backed by the
     * configured {@link SpanFactory} if none it set.
     *
     * @param config The configuration that supplies the span factory.
     * @return The default {@link CommandBusSpanFactory}.
     */
    protected CommandBusSpanFactory defaultCommandBusSpanFactory(Configuration config) {
        return defaultComponent(CommandBusSpanFactory.class, this.config).orElseGet(() -> DefaultCommandBusSpanFactory.builder().spanFactory(config.spanFactory()).build());
    }


    /**
     * Returns the default {@link EventProcessorSpanFactory}, or a
     * {@link DefaultEventProcessorSpanFactory} backed by the configured {@link SpanFactory} if none it set.
     *
     * @param config The configuration that supplies the span factory.
     * @return The default {@link EventProcessorSpanFactory}.
     */
    protected EventProcessorSpanFactory defaultEventProcessorSpanFactory(Configuration config) {
        return defaultComponent(EventProcessorSpanFactory.class, this.config).orElseGet(() -> DefaultEventProcessorSpanFactory.builder().spanFactory(config.spanFactory()).build());
    }

    protected AuthUserProvider defaultAuthUserProvider(Configuration config) {
        return defaultComponent(AuthUserProvider.class, this.config).orElseGet(() -> new AuthUserProvider() {
        });
    }

    protected JaversBuilder defaultJaversBuilder(Configuration config) {
        return defaultComponent(JaversBuilder.class, this.config).orElseGet(JaversBuilder::javers);
    }

    private MessageHandlingMemberFactory retrieveHandlerDefinition(Configuration configuration, List<AggregateConfiguration<?>> aggregateConfigurations) {
        return configuration.handlerDefinition(aggregateConfigurations.get(0).aggregateType());
    }

    @Override
    public EventProcessingConfigurer eventProcessing() {
        List<EventProcessingConfigurer> eventProcessingConfigurers = modules.stream().filter(module -> module.isType(EventProcessingConfigurer.class)).map(module -> (EventProcessingConfigurer) module.unwrap()) // It's safe to unwrap it since it isn't dependent on anything else.
                .toList();
        switch (eventProcessingConfigurers.size()) {
            case 0:
                EventProcessingModule eventProcessingModule = new EventProcessingModule();
                registerModule(eventProcessingModule);
                return eventProcessingModule;
            case 1:
                return eventProcessingConfigurers.get(0);
            default:
                throw new ConfigurationException(
                        "There are several EventProcessingConfigurers defined. "
                                + "The `eventProcessing()` method is used to retrieve a 'singleton' EventProcessingConfigurer."
                );
        }
    }


    @Override
    public Configurer configureMessageMonitor(@Nonnull Function<Configuration, BiFunction<Class<?>, String, MessageMonitor<Message<?>>>> builder) {
        messageMonitorFactoryBuilder.add((conf, type, name) -> builder.apply(conf).apply(type, name));
        return this;
    }

    @Override
    public Configurer configureMessageMonitor(@Nonnull Class<?> componentType, @Nonnull MessageMonitorFactory messageMonitorFactory) {
        messageMonitorFactoryBuilder.add(componentType, messageMonitorFactory);
        return this;
    }

    @Override
    public Configurer configureMessageMonitor(@Nonnull Class<?> componentType, @Nonnull String componentName, @Nonnull MessageMonitorFactory messageMonitorFactory) {
        messageMonitorFactoryBuilder.add(componentType, componentName, messageMonitorFactory);
        return this;
    }

    @Override
    public Configurer configureCorrelationDataProviders(@Nonnull Function<Configuration, List<CorrelationDataProvider>> correlationDataProviderBuilder) {
        correlationProviders.update(correlationDataProviderBuilder);
        return this;
    }

    @Override
    public Configurer registerModule(@Nonnull ModuleConfiguration module) {
        log.debug("Registering module [{}]", module.getClass().getSimpleName());
        if (initialized) {
            module.initialize(config);
        }
        this.modules.add(module);
        return this;
    }

    @Override
    public <C> Configurer registerComponent(@Nonnull Class<C> componentType, @Nonnull Function<Configuration, ? extends C> componentBuilder) {
        log.debug("Registering component [{}]", componentType.getSimpleName());
        components.put(componentType, new ComponentHolder<>(config, componentType.getSimpleName(), componentBuilder));
        return this;
    }

    @Override
    public Configurer registerCommandHandler(@Nonnull Function<Configuration, Object> commandHandlerBuilder) {
        messageHandlerRegistrars.add(new ComponentHolder<>(
                        //
                        () -> config,
                        // 名称
                        "CommandHandlerRegistrar",
                        // builderFunction
                        configuration -> new MessageHandlerRegistrar(() -> configuration, commandHandlerBuilder,
                                (config, commandHandler) -> new AnnotationCommandHandlerAdapter<>(commandHandler, config.parameterResolverFactory(), config.handlerDefinition(commandHandler.getClass())).subscribe(config.commandBus()))
                )
        );
        return this;
    }


    @Override
    public Configurer registerLoadHandler(@Nonnull Function<Configuration, Object> loadHandlerBuilder) {
        messageHandlerRegistrars.add(new ComponentHolder<>(() -> config, "LoadHandlerRegistrar",
                //
                configuration -> new MessageHandlerRegistrar(() -> configuration, loadHandlerBuilder,
                        //
                        (config, loadHandler) -> {
                            return new AnnotationLoadHandlerAdapter(loadHandler, config.parameterResolverFactory(), config.getComponent(HandlerAttributesPostProcessor.class), config.handlerDefinition(loadHandler.getClass()), config.getComponent(AggregateModelFactory.class)).subscribe(config.loadBus());
                        })));
        return this;
    }


    public Configurer registerLoadHandler(@Nonnull Function<Configuration, Object> commandHandlerBuilder, BiFunction<Configuration, Object, Registration> messageHandlerSubscriber) {
        messageHandlerRegistrars.add(new ComponentHolder<>(() -> config, "LoadHandlerRegistrar", configuration -> new MessageHandlerRegistrar(() -> configuration, commandHandlerBuilder, messageHandlerSubscriber)));
        return this;
    }

    @Override
    public Configurer registerPersistHandler(@Nonnull Function<Configuration, Object> loadHandlerBuilder) {
        messageHandlerRegistrars.add(new ComponentHolder<>(() -> config, "PersistHandlerRegistrar", configuration -> new MessageHandlerRegistrar(() -> configuration, loadHandlerBuilder, (config, loadHandler) -> {
            return new AnnotationPersistHandlerAdapter(loadHandler, config.parameterResolverFactory(), config.getComponent(HandlerAttributesPostProcessor.class), config.handlerDefinition(loadHandler.getClass()), config.getComponent(AggregateModelFactory.class)).subscribe(config.persistBus());
        })));

        return this;
    }

    @Override
    public Configurer registerMessageHandler(@Nonnull Function<Configuration, Object> messageHandlerBuilder) {
        ComponentHolder<Object> messageHandler = new ComponentHolder<>(() -> config, "", messageHandlerBuilder);
        Class<?> handlerClass = messageHandler.get().getClass();
        if (isCommandHandler(handlerClass)) {
            registerCommandHandler(c -> messageHandler.get());
        }
        if (isEventHandler(handlerClass)) {
            eventProcessing().registerEventHandler(c -> messageHandler.get());
        }

        return this;
    }


    @Override
    public Configurer configureEventSerializer(@Nonnull Function<Configuration, Serializer> eventSerializerBuilder) {
        eventSerializer.update(eventSerializerBuilder);
        return this;
    }

    @Override
    public Configurer configureMessageSerializer(@Nonnull Function<Configuration, Serializer> messageSerializerBuilder) {
        messageSerializer.update(messageSerializerBuilder);
        return this;
    }

    @Override
    public <A> Configurer registerConfigureAggregate(@Nonnull AggregateConfiguration<A> aggregateConfiguration) {
        return registerModule(aggregateConfiguration);
    }

    @Override
    public Configurer registerHandlerDefinition(@Nonnull BiFunction<Configuration, Class, MessageHandlingMemberFactory> handlerDefinitionClass) {
        this.handlerDefinition.update(c -> clazz -> handlerDefinitionClass.apply(c, clazz));
        return this;
    }


    @Override
    public Configurer registerHandlerEnhancerDefinition(Function<Configuration, ExecutableHandlerWrapper> handlerEnhancerBuilder) {
        this.handlerEnhancerDefinitions.add(new ComponentHolder<>(config, "HandlerEnhancerDefinition", handlerEnhancerBuilder));
        return this;
    }

    @Override
    public Configurer configureLifecyclePhaseTimeout(long timeout, TimeUnit timeUnit) {
        assertStrictPositive(timeout, "The lifecycle phase timeout should be strictly positive");
        assertNonNull(timeUnit, "The lifecycle phase time unit should not be null");
        this.lifecyclePhaseTimeout = timeout;
        this.lifecyclePhaseTimeunit = timeUnit;
        return this;
    }

    @Override
    public Configuration buildConfiguration() {
        if (!initialized) {
            verifyIdentifierFactory();
            prepareModules();
            prepareMessageHandlerRegistrars();
            invokeInitHandlers();
            initialized = true;
        }
        return config;
    }

    /**
     * Prepare the registered modules for initialization. This ensures all lifecycle handlers are registered.
     */
    protected void prepareModules() {
        modules.forEach(module -> initHandlers.add(module::initialize));
    }


    private void verifyIdentifierFactory() {
        try {
            // IdentifierFactory.getInstance();
        } catch (Exception e) {
            throw new IllegalArgumentException("The configured IdentifierFactory could not be instantiated.", e);
        }
    }

    /**
     * Prepare the registered message handlers {@link MessageHandlerRegistrar} for initialization. This ensures their
     * lifecycle handlers are registered.
     */
    protected void prepareMessageHandlerRegistrars() {
        messageHandlerRegistrars.forEach(registrar -> initHandlers.add(c -> registrar.get()));
    }

    /**
     * Calls all registered init handlers. Registration of init handlers after this invocation will result in an
     * immediate invocation of that handler.
     */
    protected void invokeInitHandlers() {

        initHandlers.forEach(h -> h.accept(config));
    }

    /**
     * Invokes all registered start handlers.
     */
    protected void invokeStartHandlers() {
        log.debug("Initiating start up");
        lifecycleState = LifecycleState.STARTING_UP;

        invokeLifecycleHandlers(startHandlers, e -> {
            log.debug("Start up is being ended prematurely due to an exception");
            String startFailure = String.format("One of the start handlers in phase [%d] failed with the following exception: ", currentLifecyclePhase);
            log.warn(startFailure, e);

            invokeShutdownHandlers();
            throw new LifecycleHandlerInvocationException(startFailure, e);
        });

        lifecycleState = LifecycleState.UP;
        log.debug("Finalized start sequence");
    }

    /**
     * Invokes all registered shutdown handlers.
     */
    protected void invokeShutdownHandlers() {
        log.debug("Initiating shutdown");
        lifecycleState = LifecycleState.SHUTTING_DOWN;

        invokeLifecycleHandlers(shutdownHandlers, e -> log.warn("One of the shutdown handlers in phase [{}] failed with the following exception: ", currentLifecyclePhase, e));

        lifecycleState = LifecycleState.DOWN;
        log.debug("Finalized shutdown sequence");
    }

    private void invokeLifecycleHandlers(TreeMap<Integer, List<LifecycleHandler>> lifecycleHandlerMap, Consumer<Exception> exceptionHandler) {
        Map.Entry<Integer, List<LifecycleHandler>> phasedHandlers = lifecycleHandlerMap.firstEntry();
        if (phasedHandlers == null) {
            return;
        }

        do {
            currentLifecyclePhase = phasedHandlers.getKey();
            log.debug("Entered {} handler lifecycle phase [{}]", lifecycleState.description, currentLifecyclePhase);

            List<LifecycleHandler> handlers = phasedHandlers.getValue();
            try {
                handlers.stream().map(LifecycleHandler::handle).map(c -> c.thenRun(NOTHING)).reduce(CompletableFuture::allOf).orElse(CompletableFuture.completedFuture(null)).get(lifecyclePhaseTimeout, lifecyclePhaseTimeunit);
            } catch (CompletionException | ExecutionException e) {
                exceptionHandler.accept(e);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                log.warn("Completion interrupted during {} phase [{}]. Proceeding to following phase", lifecycleState.description, currentLifecyclePhase);
            } catch (TimeoutException e) {
                final long lifecyclePhaseTimeoutInSeconds = TimeUnit.SECONDS.convert(lifecyclePhaseTimeout, lifecyclePhaseTimeunit);
                log.warn("Timed out during {} phase [{}] after {} second(s). Proceeding to following phase", lifecycleState.description, currentLifecyclePhase, lifecyclePhaseTimeoutInSeconds);
            }
        } while ((phasedHandlers = lifecycleHandlerMap.higherEntry(currentLifecyclePhase)) != null);
        currentLifecyclePhase = null;
    }

    /**
     * Returns the current Configuration object being built by this Configurer, without initializing it. Note that
     * retrieving objects from this configuration may lead to premature initialization of certain components.
     *
     * @return The current Configuration object being built by this Configurer.
     */
    protected Configuration getConfig() {
        return config;
    }


    public Map<Class<?>, ComponentHolder<?>> getComponents() {
        return components;
    }

    @Override
    public void onStart(int phase, LifecycleHandler startHandler) {
        onInitialize(cfg -> cfg.onStart(phase, startHandler));
    }

    @Override
    public void onShutdown(int phase, LifecycleHandler shutdownHandler) {
        onInitialize(cfg -> cfg.onShutdown(phase, shutdownHandler));
    }


}
